package exchange

import (
	"github.com/garyburd/redigo/redis"
	"github.com/ngaut/log"
	"time"
	"encoding/json"
	"fmt"
	"strconv"
	"github.com/shopspring/decimal"
	"global/libs/structs"
	"global/util"
	"global/core"
	"global/constants"
	"github.com/gorilla/websocket"
)

type SubStruct struct {
	Sub   string `json:"sub"`
	Id    string `json:"id"`
	Unsub string `json:"unsub"`
}

type decimalDepth struct {
	Bids 		[][]decimal.Decimal		`json:"bids"`
	Asks 		[][]decimal.Decimal		`json:"asks"`
}

type DepthStru struct {
	Ch 		string 				`json:"ch"`
	Tick 	decimalDepth 		`json:"tick"`
}



// topic: market.$symbol.depth.$type
type HuobiDepth struct {
	times 			int
	Slug 			string
	Pool 			*redis.Pool
	Symbol 			structs.Symbol
	Conn 			*websocket.Conn
}

func NewHuobiDepth(slug string, pool *redis.Pool) *HuobiDepth {
	return &HuobiDepth{
		Slug:	slug,
		Pool: 	pool,
	}
}

func (d *HuobiDepth) Listen() {
	for {
		_, message, err := d.Conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err) {
				log.Errorf("Error: inner error, %s", err)
			}
			log.Errorf("Error: read message error, msg: %s", err)
			break
		}

		data, err := util.UnGzip(message)
		if err != nil {
			log.Errorf("Error, unzip message error, msg: %s", err.Error())
			continue
		}

		err = d.HandleResponse(data)
		if err != nil {
			log.Errorf("Error: save depth response, msg: %s", err.Error())
			log.Errorf("Content: %s", string(message))
			continue
		}
	}
}

func (d *HuobiDepth) HandleResponse(data []byte) error {
	resp := util.JsonDecodeByte(data)
	if pongMessage, ok := resp["ping"]; ok {
		pingMap := make(map[string]interface{})
		pingMap["pong"] = pongMessage
		d.Conn.WriteJSON(pingMap)
	}

	if _, ok := resp["subbed"]; ok {
		log.Infof("subbed success, %s", resp["subbed"])
	}

	if _, ok := resp["ch"]; ok {
		err := d.doSubbedDepth(data)
		if err != nil {
			log.Errorf("Error: save depth failed: %s", err.Error())
		}
	}

	return nil
}

func (d *HuobiDepth) doSubbedDepth(message []byte) error {
	var response DepthStru
	err := json.Unmarshal(message, &response)
	if err != nil {
		return err
	}

	var depthData structs.Depth
	for _, v := range response.Tick.Bids {
		bids := []string{v[0].StringFixed(8), v[1].String(), ""}
		depthData.Bids = append(depthData.Bids, bids)
	}
	for _, v := range response.Tick.Asks {
		asks := []string{v[0].String(), v[1].String(), ""}
		depthData.Asks = append(depthData.Asks, asks)
	}

	conn := d.Pool.Get()
	defer conn.Close()

	return core.SaveDepth(conn, d.Slug, d.Symbol, depthData)
}

func (d *HuobiDepth) ListenAndAccept(symbol structs.Symbol) error {
	d.Symbol = symbol
	var err error

	for {
		d.Conn, _, err = websocket.DefaultDialer.Dial(constants.HuobiWebsocketBaseUrl, nil)
		if err != nil {
			log.Errorf("Error: dial huobi socket fialed, msg: %s", err.Error())
			// 5 秒后重试
			time.Sleep(time.Second * 5)
			continue
		}

		if err = d.Sub(symbol); err != nil {
			log.Errorf("Error: sub depth failed, symbol: %s, msg: %s",
				symbol.From + "_" + symbol.To, err.Error())
			time.Sleep(5 * time.Second)
			d.close()
			continue
		}

		d.Listen()
		d.close()
	}

	return nil
}

// 订阅深度
func (d *HuobiDepth) Sub(symbol structs.Symbol) error {
	var message SubStruct
	message.Id = strconv.Itoa(time.Now().Nanosecond())
	message.Sub = fmt.Sprintf("market.%s.depth.step0", symbol.From + symbol.To)
	return d.Conn.WriteJSON(message)
}

func (d *HuobiDepth) close() {
	if d.Conn != nil {
		d.Conn.Close()
	}
}
